package com.ruben.simplescaffold.utils.steam;

import java.util.Comparator;
import java.util.Optional;
import java.util.function.*;
import java.util.stream.Stream;

/**
 * Steam是理财软件(不是，是抄的jdk的Stream) 为集合提供了一些易用api，它让开发人员能使用声明式编程的方式去编写代码
 * 它分为中间操作和结束操作
 * 中间操作分为 无状态中间操作(表示不用等 所有元素的当前操作执行完 就可以执行的操作，不依赖之前历史操作的流的状态) 与 有状态中间操作(表示需要等 所有元素的当前操作执行完 才能执行的操作,依赖之前历史操作的流的状态)
 * 结束操作分为 短路结束操作(表示不用等 所有元素的当前操作执行完 就可以执行的操作) 与 非短路结束操作(表示需要等 所有元素的当前操作执行完 才能执行的操作)
 * Steam流只有在 结束操作 时才会真正触发执行以往的 中间操作
 *
 * @param <T> 流元素类型
 * @author jdk
 * @see IntSteam
 * @see LongSteam
 * @see DoubleSteam
 * @since 1.8
 */
public interface Steam<T> extends BaseSteam<T, Steam<T>> {

    /**
     * 过滤元素，返回与指定断言匹配的此流的元素组成的流
     * 这是一个无状态中间操作
     *
     * @param predicate 断言
     * @return 返回叠加过滤操作后的Steam
     */
    Steam<T> filter(Predicate<? super T> predicate);

    /**
     * 返回与指定函数将元素作为参数执行的结果组成的流。
     * 这是一个无状态中间操作
     *
     * @param mapper 指定的函数
     * @param <R>    函数执行后返回的类型
     * @return 返回叠加操作后的Steam
     */
    <R> Steam<R> map(Function<? super T, ? extends R> mapper);

    /**
     * 和map一样，只不过函数的返回值必须为int类型
     * 这是一个无状态中间操作
     *
     * @param mapper 返回值为int类型的函数
     * @return 叠加操作后元素类型全为int的Steam流
     */
    IntSteam mapToInt(ToIntFunction<? super T> mapper);

    /**
     * 和map一样，只不过函数的返回值必须为long类型
     * 这是一个无状态中间操作
     *
     * @param mapper 返回值为long类型的函数
     * @return 叠加操作后元素类型全为long的Steam流
     */
    LongSteam mapToLong(ToLongFunction<? super T> mapper);

    /**
     * 和map一样，只不过函数的返回值必须为double类型
     * 这是一个无状态中间操作
     *
     * @param mapper 返回值为double类型的函数
     * @return 叠加操作后元素类型全为double的Steam流
     */
    DoubleSteam mapToDouble(ToDoubleFunction<? super T> mapper);

    /**
     * 拆分流操作，将原有流元素执行mapper操作，返回多个流所有元素组成的流
     * 这是一个无状态中间操作
     * 例如，将Steam<UserDetail>里所有user的id和parentId组合在一起，形成一个新的流:
     * <pre>{@code
     *     Steam<Long> ids = Steam.of(UserDetail.builder().id(1L).parentId(2L).build()).flatMap(user -> Steam.of(user.getId(), user.getParentId()));
     * }</pre>
     *
     * @param mapper 操作，返回Steam
     * @param <R>    拆分后流的元素类型
     * @return 返回叠加拆分操作后的Steam流
     */
    <R> Steam<R> flatMap(Function<? super T, ? extends Steam<? extends R>> mapper);

    /**
     * 拆分流操作，将原有流元素执行mapper操作，返回多个流所有元素组成的流
     * 这是一个无状态中间操作
     *
     * @param mapper 操作，返回IntSteam
     * @return 返回叠加拆分操作后的IntSteam
     */
    IntSteam flatMapToInt(Function<? super T, ? extends IntSteam> mapper);

    /**
     * 拆分流操作，将原有流元素执行mapper操作，返回多个流所有元素组成的流
     * 这是一个无状态中间操作
     * 例如，将Steam<UserDetail>里所有user的id和parentId组合在一起，形成一个新的流:
     * <pre>{@code
     *     LongSteam longSteam = Steam.of(UserDetail.builder().id(1L).parentId(2L).build()).flatMapToLong(user -> Steam.of(user.getId(), user.getParentId()).mapToLong(Long::valueOf));
     * }</pre>
     *
     * @param mapper 操作，返回LongSteam
     * @return 返回叠加拆分操作后的LongSteam
     */
    LongSteam flatMapToLong(Function<? super T, ? extends LongSteam> mapper);

    /**
     * 拆分流操作，将原有流元素执行mapper操作，返回多个流所有元素组成的流
     * 这是一个无状态中间操作
     *
     * @param mapper 操作，返回DoubleSteam
     * @return 返回叠加拆分操作后的DoubleSteam
     */
    DoubleSteam flatMapToDouble(Function<? super T, ? extends DoubleSteam> mapper);

    /**
     * 返回一个具有去重特征的流 非并行流(顺序流)下对于重复元素，保留遇到顺序中最先出现的元素，并行流情况下不能保证具体保留哪一个
     * 这是一个有状态中间操作
     *
     * @return 一个具有去重特征的流
     */
    Steam<T> distinct();

    /**
     * 返回一个元素按自然顺序排序的流 如果此流的元素不是Comparable ，则在执行终端操作时可能会抛出java.lang.ClassCastException 。对于顺序流，排序是稳定的。 对于无序流，没有稳定性保证。
     * 这是一个有状态中间操作
     *
     * @return 一个元素按自然顺序排序的流
     */
    Steam<T> sorted();

    /**
     * 返回一个元素按指定的Comparator排序的流 如果此流的元素不是Comparable ，则在执行终端操作时可能会抛出java.lang.ClassCastException 。对于顺序流，排序是稳定的。 对于无序流，没有稳定性保证。
     * 这是一个有状态中间操作
     *
     * @param comparator 排序规则
     * @return 一个元素按指定的Comparator排序的流
     */
    Steam<T> sorted(Comparator<? super T> comparator);

    /**
     * 返回与指定函数将元素作为参数执行后组成的流。
     * 这是一个无状态中间操作
     *
     * @param action 指定的函数
     * @return 返回叠加操作后的Steam
     */
    Steam<T> peek(Consumer<? super T> action);

    /**
     * 返回截取后面一些元素的流
     * 这是一个短路状态中间操作
     *
     * @param maxSize 元素截取后的个数
     * @return 截取后的流
     */
    Steam<T> limit(long maxSize);

    /**
     * 返回丢弃前面n个元素后的剩余元素组成的流，如果当前元素个数小于n，则返回一个元素为空的流
     * 这是一个有状态中间操作
     *
     * @param n 需要丢弃的元素个数
     * @return 丢弃前面n个元素后的剩余元素组成的流
     */
    Stream<T> skip(long n);

    /**
     * 对流里面的每一个元素执行一个操作
     * 这是一个终端操作
     *
     * @param action 操作
     */
    void forEach(Consumer<? super T> action);


    /**
     * 对流里面的每一个元素按照顺序执行一个操作
     * 这是一个终端操作
     *
     * @param action 操作
     */
    void forEachOrdered(Consumer<? super T> action);

    /**
     * 返回一个包含此流元素的数组
     * 这是一个终端操作
     *
     * @return 包含此流元素的数组
     */
    Object[] toArray();

    /**
     * 返回一个包含此流元素的指定的数组
     *
     * @param generator 这里的IntFunction的参数是元素的个数，返回值为数组类型
     * @param <A>       给定的数组类型
     * @return 包含此流元素的指定的数组
     * @throws ArrayStoreException 如果元素转换失败，例如不是该元素类型及其父类，则抛出该异常
     * @apiNote 例如以下代码编译正常，但运行时会抛出 {@link ArrayStoreException}
     * <pre>{@code
     * String[] strings = Stream.<Integer>builder().add(1).build().toArray(String[]::new);
     * }</pre>
     */
    <A> A[] toArray(IntFunction<A[]> generator);


    /**
     * 对元素进行聚合，并返回聚合后的值，相当于在for循环里写sum=sum+ints[i]
     * 这是一个终端操作
     *
     * @param identity    初始值，还用于限定泛型
     * @param accumulator 你想要的聚合操作
     * @return 聚合计算后的值
     * @apiNote 求和、最小值、最大值、平均值和转换成一个String字符串均为聚合操作
     * 例如这里对int进行求和可以写成：
     *
     * <pre>{@code
     *     Integer sum = integers.reduce(0, (a, b) -> a+b);
     * }</pre>
     * <p>
     * 或者写成:
     *
     * <pre>{@code
     *     Integer sum = integers.reduce(0, Integer::sum);
     * }</pre>
     */
    T reduce(T identity, BinaryOperator<T> accumulator);

    /**
     * 对元素进行聚合，并返回聚合后用 {@link Optional}包裹的值，相当于在for循环里写sum=sum+ints[i]
     * 该操作相当于：
     * <pre>{@code
     *     boolean foundAny = false;
     *     T result = null;
     *     for (T element : this stream) {
     *         if (!foundAny) {
     *             foundAny = true;
     *             result = element;
     *         }
     *         else
     *             result = accumulator.apply(result, element);
     *     }
     *     return foundAny ? Optional.of(result) : Optional.empty();
     * }</pre>
     * 但它不局限于顺序执行，例如并行流等情况下
     * 这是一个终端操作
     *
     * @param accumulator 你想要的聚合操作
     * @return 聚合后用 {@link Optional}包裹的值
     * @throws NullPointerException 如果给定的聚合操作中执行后结果为空，并用于下一次执行，则抛出该异常
     * @apiNote 例如以下场景抛出 NPE ：
     * <pre>{@code
     *      Optional<Integer> reduce = Stream.<Integer>builder().add(1).add(1).build().reduce((a, b) -> null);
     * }</pre>
     * @see #reduce(Object, BinaryOperator)
     * @see #min(Comparator)
     * @see #max(Comparator)
     */
    Optional<T> reduce(BinaryOperator<T> accumulator);

    /**
     * 对元素进行聚合，并返回聚合后的值
     * 这是一个终端操作
     *
     * @param identity    初始值
     * @param accumulator 累加器，具体为你要的聚合操作
     * @param combiner    用于并行流时组合多个结果
     * @param <U>         初始值
     * @return 聚合操作的结果
     * @see #reduce(BinaryOperator)
     * @see #reduce(Object, BinaryOperator)
     */
    <U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);


    /**
     * 对元素进行收集，并返回收集后的容器
     * 这是一个终端操作
     *
     * @param supplier    提供初始值的函数式接口，一般可以传入构造参数
     * @param accumulator 具体收集操作
     * @param combiner    用于并行流时组合多个结果
     * @param <R>         用于收集元素的容器，大多是集合
     * @return 收集后的容器
     * <pre>{@code
     *  List<Integer> collect = Stream.iterate(1, i -> ++i).limit(10).collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
     * }</pre>
     */
    <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);


    /**
     * 对元素进行收集，并返回收集后的元素
     * 这是一个终端操作
     *
     * @param collective 收集器
     * @param <R>        容器类型
     * @param <A>        具体操作时容器类型，例如 {@code List::add} 时它为 {@code List}
     * @return 收集后的容器
     */
//    <R, A> R collect(Collective<? super T, A, R> collective);

    Optional<T> min(Comparator<? super T> comparator);

    Optional<T> max(Comparator<? super T> comparator);

    long count();

    boolean anyMatch(Predicate<? super T> predicate);

    boolean allMatch(Predicate<? super T> predicate);

    boolean noneMatch(Predicate<? super T> predicate);

    Optional<T> findFirst();

    Optional<T> findAny();

}
